﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;

namespace Engine
{
    /**this class represent 3 dimensional cubic lattice.*/
    public class Cubic : Lattice
    {

        private int[, ,] grid;

        private const int dimension = 3;

        private int xLenght;

        private int yLenght;

        private int zLenght;

        private Random random;

        /**this property reserved for counting of cutted points */
        private int cuttedPoints = 0;

        /**this propertry reserved for counting of cutted _bonds */
        private int cuttedBonds = 0;

        /**this property contain all working points in grid */
        private IList<Point> pointsList;

        private Bonds bonds;

        public override int CuttedPoints
        {
            get { return cuttedPoints; }
        }

        public override int WorkingPoints
        {
            get { return pointsList.Count; }
        }

        public override int CuttedBonds
        {
            get { return cuttedBonds; }
        }

        public override int WorkingBonds
        {
            get { return bonds.Count; }
        }

        public override double Conductance
        {
            get { throw new NotImplementedException(); }
        }

        public Cubic()
            : base()
        {
            xLenght = 6;
            yLenght = 6;
            zLenght = 6;
            grid = new int[xLenght + 2, yLenght + 2, zLenght];
            random = new Random();
            fillArray(grid);
            fillPointList();
            fillBondsList();
        }

        /**This method fill list of ponts.*/
        private void fillPointList()
        {
            pointsList = new List<Point>();
            for (int x = 0; x < xLenght + 2; x++)
            {
                for (int y = 0; y < yLenght + 2; y++)
                {
                    for (int z = 0; z < zLenght; z++)
                    {
                        if (z == 0)
                        {
                            if (y != 0 && y != yLenght + 1)
                            {
                                Point point = new Point(x, y, z);
                                pointsList.Add(point);
                            }
                        }
                        else
                        {
                            Point point = new Point(x, y, z);
                            pointsList.Add(point);
                        }
                    }
                }
            }
        }

        /**
         * this method fill list of _bonds
         * */
        private void fillBondsList()
        {
            //create new object. Of couse we can use old object. 
            bonds = new Bonds();
            for (int z = 0; z < zLenght; z++) //walk in z directions.
            {
                for (int y = 0; y < yLenght + 2; y++) //walk in y direction
                {
                    for (int x = 0; x < xLenght + 2; x++) //walk in x direction
                    {
                        if (z > 0) // condition for never removed row.
                        {
                            if (x != xLenght + 1)
                            {
                                Bond xBond = new Bond(x, y, z, x + 1, y, z);
                                bonds.Add(xBond);
                            }
                        }
                        else
                        {
                            if (y != 0 && y != yLenght + 1)
                            {
                                if (x != xLenght + 1)
                                {
                                    Bond xBond = new Bond(x, y, z, x + 1, y, z);
                                    bonds.Add(xBond);
                                }
                            }
                        }
                        if (y != yLenght + 1) //check if this is not the last row in y direction. 
                        {
                            Bond yBond = new Bond(x, y, z, x, y + 1, z);
                            bonds.Add(yBond);
                        }
                        if (z != zLenght - 1)
                        {
                            Bond zBond = new Bond(x, y, z, x, y, z + 1);
                            bonds.Add(zBond);
                        }
                    }
                }
            }
        }


        /*
        * in this method we fill array with new data 
        */
        private void fillArray(int[, ,] array)
        {
            int x = array.GetLength(0);
            int y = array.GetLength(1);
            int z = array.GetLength(2);
            for (int i = 0; i < x; i++)
            {
                for (int k = 0; k < y; k++)
                {
                    for (int p = 0; p < z; p++)
                    {
                        array[i, k, p] = 1;
                    }
                }
            }
        }

        public override void CutBond()
        {
            int size = bonds.Count;
            int index = random.Next(size);
            //what i should do with this bond
            Bond bondToCut = bonds[index];
            //!TODO - stupid logic at this place 
            bonds.Remove(bondToCut);
            cuttedBonds++;
        }

        public override void CutPoint()
        {
            int size = pointsList.Count;
            int index = random.Next(size);
            Point pointToCut = (Point)pointsList[index];
            pointsList.Remove(pointToCut);
            grid[pointToCut.X, pointToCut.Y, pointToCut.Z] = 0;
            cuttedPoints++;
        }

        /**check is two point connect with each other */
        public override bool IsConduct()
        {
            int[, ,] check = new int[xLenght + 2, yLenght + 2, zLenght];
            Array.Copy(grid, check, (xLenght + 2) * (yLenght + 2) * zLenght);
            IList<Point> edgePoints = new List<Point>();

            for (int x = 0; x < xLenght + 2; x++)
            {
                check[x, 0, 0] = 2; //initial point of our research.
                edgePoints.Add(new Point(x, 0, 0));
            }

            while (edgePoints.Count > 0) //we have new points
            {
                edgePoints = discoverNewEdgePoints(edgePoints, check);
            }

            for (int x = 0; x < xLenght + 2; x++)
            {
                if (check[x, yLenght + 1, 0] == 2)
                {
                    return true;
                }
            }

            return false;
        }

        /**find new generation of edge points.*/
        private IList<Point> discoverNewEdgePoints(IList<Point> points, int[, ,] grid)
        {
            IList<Point> newPoints = new List<Point>();
            foreach (Point point in points)
            {
                int x = point.X;
                int y = point.Y;
                int z = point.Z;
                if (z == 0)
                {
                    if (isConnect(grid, x, y, z, x, y, z + 1))
                    {
                        Point newPoint = new Point(x, y, z + 1);
                        newPoints.Add(newPoint);
                        grid[x, y, z + 1] = 2;
                    }
                }
                else if (z == zLenght - 1)
                {
                    if (isConnect(grid, x, y, z, x, y, z - 1))
                    {
                        Point newPoint = new Point(x, y, z - 1);
                        newPoints.Add(newPoint);
                        grid[x, y, z - 1] = 2;
                    }
                }
                else
                {
                    if (isConnect(grid, x, y, z, x, y, z + 1))
                    {
                        Point newPoint = new Point(x, y, z + 1);
                        newPoints.Add(newPoint);
                        grid[x, y, z + 1] = 2;
                    }
                    if (isConnect(grid, x, y, z, x, y, z - 1))
                    {
                        Point newPoint = new Point(x, y, z - 1);
                        newPoints.Add(newPoint);
                        grid[x, y, z - 1] = 2;
                    }
                }
                if (x == 0)
                {
                    if (isConnect(grid, x, y, z, x + 1, y, z))
                    {
                        Point newPoint = new Point(x + 1, y, z);
                        newPoints.Add(newPoint);
                        grid[x + 1, y, z] = 2;
                    }
                }
                else if (x == xLenght + 1)
                {
                    if (isConnect(grid, x, y, z, x - 1, y, z))
                    {
                        Point newPoint = new Point(x - 1, y, z);
                        newPoints.Add(newPoint);
                        grid[x - 1, y, z] = 2;
                    }
                }
                else
                {
                    if (isConnect(grid, x, y, z, x + 1, y, z))
                    {
                        Point newPoint = new Point(x + 1, y, z);
                        newPoints.Add(newPoint);
                        grid[x + 1, y, z] = 2;
                    }
                    if (isConnect(grid, x, y, z, x - 1, y, z))
                    {
                        Point newPoint = new Point(x - 1, y, z);
                        newPoints.Add(newPoint);
                        grid[x - 1, y, z] = 2;
                    }
                }

                if (y == 0)
                {
                    if (isConnect(grid, x, y, z, x, y + 1, z))
                    {
                        Point newPoint = new Point(x, y + 1, z);
                        newPoints.Add(newPoint);
                        grid[x, y + 1, z] = 2;
                    }
                }
                else if (y == yLenght + 1)
                {
                    if (isConnect(grid, x, y, z, x, y - 1, z))
                    {
                        Point newPoint = new Point(x, y - 1, z);
                        newPoints.Add(newPoint);
                        grid[x, y - 1, z] = 2;
                    }
                }
                else
                {
                    if (isConnect(grid, x, y, z, x, y + 1, z))
                    {
                        Point newPoint = new Point(x, y + 1, z);
                        newPoints.Add(newPoint);
                        grid[x, y + 1, z] = 2;
                    }
                    if (isConnect(grid, x, y, z, x, y - 1, z))
                    {
                        Point newPoint = new Point(x, y - 1, z);
                        newPoints.Add(newPoint);
                        grid[x, y - 1, z] = 2;
                    }
                }
            }
            return newPoints;
        }


        private bool isConnect(int[, ,] arr, int x1, int y1, int z1, int x2, int y2, int z2)
        {
            if (bonds.isConduct(x1, y1, z1, x2, y2, z2) && arr[x2, y2, z2] == 1)
            {
                return true;
            }
            return false;
        }



        /** 
         *<summary>reset internal state of lattice for next use.</summary>
         */
        public override void Reset()
        {
            fillArray(grid);
            fillPointList();
            fillBondsList();
            cuttedBonds = 0;
            cuttedPoints = 0;
        }

        public override int Dimension
        {
            get { return dimension; }
        }

        public override int X
        {
            get
            {
                return xLenght;
            }
            set
            {
                xLenght = value;
                grid = new int[xLenght + 2, yLenght + 2, zLenght];
                Reset();
            }
        }

        public override int Y
        {
            get
            {
                return yLenght;
            }
            set
            {
                yLenght = value;
                grid = new int[xLenght + 2, yLenght + 2, zLenght];
                Reset();
            }
        }

        public override int Z
        {
            get
            {
                return zLenght;
            }
            set
            {
                zLenght = value;
                grid = new int[xLenght + 2, yLenght + 2, zLenght];
                Reset();
            }
        }

        public override IList<Point> Points
        {
            get { throw new NotImplementedException(); }
        }

        public override IList<Bond> Bonds
        {
            get { throw new NotImplementedException(); }
        }
    }
}
